package com.beta.cust.bind.datasync.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.beta.cat.service.RedissonService;
import com.beta.cust.bind.datasync.constant.BusinessConstant;
import com.beta.cust.bind.datasync.constant.CacheConstans;
import com.beta.cust.bind.datasync.dto.PersonnelDTO;
import com.beta.cust.bind.datasync.dto.SelectReq;
import com.beta.cust.bind.datasync.dto.WeixinUserDetailDto;
import com.beta.cust.bind.datasync.mapper.entity.DepartmentInfo;
import com.beta.cust.bind.datasync.mapper.entity.EmpCustRef;
import com.beta.cust.bind.datasync.mapper.entity.EmpDepRef;
import com.beta.cust.bind.datasync.mapper.entity.EmployeeInfo;
import com.beta.cust.bind.datasync.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author：chenlei
 * @Date：2024/3/13 16:44
 */
@Service
@Slf4j
public class SyncWechatWorkEmployeeInfoImpl implements ISyncWechatWorkEmployeeInfo {

    @Autowired
    private WechatWorkServiceImpl wechatWorkService;

    @Autowired
    private IDepartmentInfoService departmentInfoService;

    @Autowired
    private IEmployeeInfoService employeeInfoService;

    @Autowired
    private IEmpDepRefService empDepRefService;

    @Autowired
    private IEmpCustRefService empCustRefService;

    @Autowired
    private ICustInfoService custInfoService;

    @Autowired
    private RedissonService redissonService;

    @Autowired
    private AuthCorpInfoService corpInfoService;

    /**
     * 同步部门员工
     * 参考地址：https://developer.work.weixin.qq.com/document/path/90201
     *
     * @param corpId -> 企业id -> 是否必须：是
     */
    @Override
    public boolean syncWechatWorkEmployeeInfo(String corpId) {
        String lockKey = String.format(CacheConstans.SYNC_EMPLOYEE_INFO, corpId);
        try {
            if (redissonService.tryLock(lockKey, CacheConstans.WAIT_TIME)) {//一直锁到同步结束
                LocalDateTime excuteBatchTime = LocalDateTime.now();
                SelectReq req = new SelectReq();
                req.setPageSize(10);
                while (true) {
                    //1. 分页查询部门信息
                    IPage<DepartmentInfo> departmentInfoIPage = departmentInfoService.selectPage(req);
                    List<DepartmentInfo> departmentInfoList = departmentInfoIPage.getRecords();
                    if (CollectionUtils.isEmpty(departmentInfoList)) {
                        break;
                    }
                    req.setId(departmentInfoList.get(departmentInfoList.size() - 1).getId());
                    //2. 循环同步每个部门的员工个人信息
                    for (DepartmentInfo departmentInfo : departmentInfoList) {
                        String agentId = null;
                        if (!departmentInfo.getCorpId().equals(corpId)) {//不是上游企业
                            agentId = corpInfoService.getAgentIdByCorpId(departmentInfo.getCorpId());
                        }
                        PersonnelDTO dto = wechatWorkService.employeeDetailList(false, departmentInfo.getDepId(), departmentInfo.getCorpId(), agentId);
                        List<PersonnelDTO.UserlistDTO> detailDTOS = dto.getUserlist();
                        //返回员工不为空时，保存至数据库
                        if (!CollectionUtils.isEmpty(detailDTOS)) {
                            syncEmployeeList(detailDTOS, departmentInfo.getCorpId());
                            syncDepEmployeeList(detailDTOS, departmentInfo.getDepId(), departmentInfo.getCorpId());
                        }
                    }

                    //3.全部同步结束后，更改更新时间在任务启动之前的数据为软删除
                    afterDeleteEmp(excuteBatchTime);
                    afterDeleteDepEmp(excuteBatchTime);
                }
            } else {
                log.info("当前已有定时任务运行中...");
                return false;
            }
        } catch (Exception e) {
            log.error("syncWechatWorkEmployeeInfo error：{}", e.getMessage(), e);
            return false;
        } finally {
            redissonService.unlock(lockKey);
        }
        return true;
    }

    @Override
    public void createEmployee(String userID, String corpId, String agentId) {
        //1.查询数据库历史记录 是否存在改客户 不存在则新增 存在则修改
        Integer count = employeeInfoService.count(new LambdaQueryWrapper<EmployeeInfo>()
                .eq(EmployeeInfo::getCorpId, corpId)
                .eq(EmployeeInfo::getUserId, userID));
        if (count > 0) {
            updateEmployee(userID, corpId, agentId);
            return;
        }
        //2.历史记录不存在客户 新增
        EmployeeInfo employeeInfo = new EmployeeInfo();
        //3.调用企微接口-获取员工详细信息
        WeixinUserDetailDto user = wechatWorkService.employeeDetail(userID, corpId, agentId);
        //4.员工对象赋值 更新数据
        BeanUtils.copyProperties(user, employeeInfo);
        employeeInfo.setCorpId(corpId);
        employeeInfo.setDelFlag(BusinessConstant.IS_NOT_DEL_FLAG);
        employeeInfo.setIsLeaderInDept(employeeInfo.getIsLeaderInDept().substring(1, employeeInfo.getIsLeaderInDept().length() - 1));
        employeeInfoService.save(employeeInfo);
        //5.新增员工部门关联表
        String department = user.getDepartment().substring(1, user.getDepartment().length() - 1);
        if (StringUtils.isNotBlank(department)) {
            //新增所有部门
            List<EmpDepRef> depRefs = new ArrayList<>();
            for (String dep : department.split(",")) {
                buildEmpDepRefMethod(depRefs, corpId, Long.parseLong(dep), userID);
            }
            empDepRefService.saveBatch(depRefs);
        }
    }

    @Override
    public void updateEmployee(String userID, String corpId, String agentId) {
        log.info("员工回调-更新员工:{}", userID);
        EmployeeInfo employeeInfo = new EmployeeInfo();
        //1.调用企微接口-获取员工详细信息
        WeixinUserDetailDto user = wechatWorkService.employeeDetail(userID, corpId, agentId);
        //2.员工对象赋值 更新数据
        BeanUtils.copyProperties(user, employeeInfo);
        employeeInfo.setDelFlag(BusinessConstant.IS_NOT_DEL_FLAG);
        employeeInfo.setIsLeaderInDept(employeeInfo.getIsLeaderInDept().substring(1, employeeInfo.getIsLeaderInDept().length() - 1));
        employeeInfoService.update(employeeInfo, new LambdaQueryWrapper<EmployeeInfo>()
                .eq(EmployeeInfo::getCorpId, corpId)
                .eq(EmployeeInfo::getUserId, userID));
        //3.查询员工的部门即员工和部门的关系，不可能没有数据
        List<EmpDepRef> empDepRefList = empDepRefService.list(new LambdaQueryWrapper<EmpDepRef>()
                .eq(EmpDepRef::getCorpId, corpId)
                .eq(EmpDepRef::getEmpId, userID)
                .eq(EmpDepRef::getDelFlag, BusinessConstant.IS_NOT_DEL_FLAG));
        List<Long> depIdList = empDepRefList.stream().map(EmpDepRef::getDepId).collect(Collectors.toList());
        //4.如果员工部门有变化才更新部门，否则不更新员工的部门即不更新员工和部门的关系
        String dbDep = StrUtil.join(",", depIdList);
        String department = user.getDepartment().substring(1, user.getDepartment().length() - 1);
        if (StringUtils.isNotEmpty(department)) {
            if (!StrUtil.equals(dbDep, department)) {
                log.info("员工回调-员工部门信息有更改");
                // 删除旧的部门-员工信息
                empDepRefService.remove(new LambdaQueryWrapper<EmpDepRef>()
                        .eq(EmpDepRef::getCorpId, corpId)
                        .eq(EmpDepRef::getEmpId, userID));
                // 新增新的部门和员工的关系
                List<EmpDepRef> depRefs = new ArrayList<>();
                for (String dep : department.split(",")) {
                    buildEmpDepRefMethod(depRefs, corpId, Long.parseLong(dep), userID);
                }
                empDepRefService.saveBatch(depRefs);
            }
        }
    }

    @Override
    public void deleteEmployee(String userID, String corpId) {
        log.info("员工回调-删除员工:{}", userID);
        //1.删除员工信息
        EmployeeInfo employeeInfo = new EmployeeInfo();
        employeeInfo.setDelFlag(BusinessConstant.IS_DEL_FLAG);
        employeeInfoService.update(employeeInfo, new LambdaQueryWrapper<EmployeeInfo>()
                .eq(EmployeeInfo::getCorpId, corpId)
                .eq(EmployeeInfo::getUserId, userID));
        //2.删除员工-部门信息
        EmpDepRef empDepRef = new EmpDepRef();
        empDepRef.setDelFlag(BusinessConstant.IS_DEL_FLAG);
        empDepRefService.update(empDepRef, new LambdaQueryWrapper<EmpDepRef>()
                .eq(EmpDepRef::getCorpId, corpId)
                .eq(EmpDepRef::getEmpId, userID));
        //3.删除员工-客户信息
        EmpCustRef empCustRef = new EmpCustRef();
        empCustRef.setDelFlag(BusinessConstant.IS_DEL_FLAG);
        empCustRefService.update(empCustRef, new LambdaQueryWrapper<EmpCustRef>()
                .eq(EmpCustRef::getCorpId, corpId)
                .eq(EmpCustRef::getUserId, userID));
    }


    private void syncEmployeeList(List<PersonnelDTO.UserlistDTO> detailDTOS, String corpId) {
        //1.获取所有的userId集合
        Set<String> userIdList = new HashSet<>();
        for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
            userIdList.add(dto.getUserid());
        }
        //2.根据corpId,depId查询数据库
        List<EmployeeInfo> existList = employeeInfoService.list(new LambdaQueryWrapper<EmployeeInfo>().eq(EmployeeInfo::getCorpId, corpId)
                .in(EmployeeInfo::getUserId, userIdList));
        List<EmployeeInfo> addList = new ArrayList<>();
        List<EmployeeInfo> updateList = new ArrayList<>();
        //3.1数据库全部数据都不存在 则直接新增
        if (CollectionUtils.isEmpty(existList)) {
            addList = getEmployeeList(detailDTOS, corpId);
        } else {
            List<String> existUserIdList = existList.stream().map(EmployeeInfo::getUserId).collect(Collectors.toList());
            Map<String, EmployeeInfo> empMap = existList.stream().collect(Collectors.toMap(EmployeeInfo::getUserId, a -> a));
            //3.2数据库存在则新增 数据库不存在则新增
            for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
                if (existUserIdList.contains(dto.getUserid())) {
                    updateList.add(getEmployee(dto, empMap.get(dto.getUserid()), corpId));
                } else {
                    EmployeeInfo employeeInfo = new EmployeeInfo();
                    addList.add(getEmployee(dto, employeeInfo, corpId));
                }
            }
        }
        if (!CollectionUtils.isEmpty(addList)) {
            employeeInfoService.saveBatch(addList);
        }
        if (!CollectionUtils.isEmpty(updateList)) {
            employeeInfoService.updateBatchById(updateList);
        }
    }


    private void syncDepEmployeeList(List<PersonnelDTO.UserlistDTO> detailDTOS, long deptId, String corpId) {
        //1.获取所有的userId集合
        Set<String> userIdList = new HashSet<>();
        for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
            userIdList.add(dto.getUserid());
        }
        //2.根据corpId,depId查询数据库
        List<EmpDepRef> existList = empDepRefService.list(new LambdaQueryWrapper<EmpDepRef>()
                .eq(EmpDepRef::getCorpId, corpId)
                .eq(EmpDepRef::getDepId, deptId)
                .in(EmpDepRef::getEmpId, userIdList));
        List<EmpDepRef> addList = new ArrayList<>();
        List<EmpDepRef> updateList = new ArrayList<>();
        //3.1数据库全部数据都不存在 则直接新增
        if (CollectionUtils.isEmpty(existList)) {
            addList = getDepEmployeeList(detailDTOS, deptId, corpId);
        } else {
            List<String> existUserIdList = existList.stream().map(EmpDepRef::getEmpId).collect(Collectors.toList());
            Map<String, EmpDepRef> empDepMap = existList.stream().collect(Collectors.toMap(EmpDepRef::getEmpId, a -> a));
            //3.2数据库存在则新增 数据库不存在则新增
            for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
                if (existUserIdList.contains(dto.getUserid())) {
                    updateList.add(getDepEmp(dto, empDepMap.get(dto.getUserid()), deptId, corpId));
                } else {
                    EmpDepRef empDepRef = new EmpDepRef();
                    addList.add(getDepEmp(dto, empDepRef, deptId, corpId));
                }
            }
        }
        if (!CollectionUtils.isEmpty(addList)) {
            empDepRefService.saveBatch(addList);
        }
        if (!CollectionUtils.isEmpty(updateList)) {
            empDepRefService.updateBatchById(updateList);
        }
    }

    private void afterDeleteEmp(LocalDateTime syncTime) {
        try {
            //避免数据太少 快速结束 误删除
            Thread.sleep(2000);
            EmployeeInfo employeeInfo = new EmployeeInfo();
            employeeInfo.setDelFlag(BusinessConstant.IS_DEL_FLAG);
            employeeInfoService.update(employeeInfo, new LambdaQueryWrapper<EmployeeInfo>().lt(EmployeeInfo::getUpdateTime, Timestamp.valueOf(syncTime)));
        } catch (Exception e) {
            log.error("afterDeleteEmp error:{}", e.getMessage(), e);
        }

    }

    private void afterDeleteDepEmp(LocalDateTime syncTime) {
        try {
            //避免数据太少 快速结束 误删除
            Thread.sleep(2000);
            EmpDepRef empDepRef = new EmpDepRef();
            empDepRef.setDelFlag(BusinessConstant.IS_DEL_FLAG);
            empDepRefService.update(empDepRef, new LambdaQueryWrapper<EmpDepRef>().lt(EmpDepRef::getUpdateTime, Timestamp.valueOf(syncTime)));
        } catch (Exception e) {
            log.error("afterDeleteDepEmp error:{}", e.getMessage(), e);
        }

    }

    private List<EmployeeInfo> getEmployeeList(List<PersonnelDTO.UserlistDTO> detailDTOS, String corpId) {
        List<EmployeeInfo> employeeInfoList = new ArrayList<>();
        for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
            EmployeeInfo employeeInfo = new EmployeeInfo();
            employeeInfo = getEmployee(dto, employeeInfo, corpId);
            employeeInfoList.add(employeeInfo);
        }
        return employeeInfoList;
    }

    private EmployeeInfo getEmployee(PersonnelDTO.UserlistDTO dto, EmployeeInfo employeeInfo, String corpId) {
        employeeInfo.setCorpId(corpId);
        employeeInfo.setUserId(dto.getUserid());
        employeeInfo.setName(dto.getName());
        employeeInfo.setMobile(dto.getMobile());
        employeeInfo.setPosition(dto.getPosition());
        employeeInfo.setGender(dto.getGender());
        employeeInfo.setEmail(dto.getEmail());
        employeeInfo.setIsLeaderInDept(StrUtil.join(",", dto.getIsLeaderInDept()));
        employeeInfo.setAvatar(dto.getAvatar());
        employeeInfo.setThumbAvatar(dto.getThumbAvatar());
        employeeInfo.setTelephone(dto.getTelephone());
        employeeInfo.setAlias(dto.getAlias());
        employeeInfo.setStatus(dto.getStatus());
        employeeInfo.setAddress(dto.getAddress());
        employeeInfo.setOpenUserid(dto.getOpenUserid());
        employeeInfo.setExternalPosition(dto.getExternalPosition());
        employeeInfo.setDelFlag(BusinessConstant.IS_NOT_DEL_FLAG);
        employeeInfo.setUpdateTime(Timestamp.valueOf(LocalDateTime.now()));
        return employeeInfo;
    }

    private List<EmpDepRef> getDepEmployeeList(List<PersonnelDTO.UserlistDTO> detailDTOS, long depId, String corpId) {
        List<EmpDepRef> empDepRefs = new ArrayList<>();
        for (PersonnelDTO.UserlistDTO dto : detailDTOS) {
            EmpDepRef empDepRef = new EmpDepRef();
            empDepRef = getDepEmp(dto, empDepRef, depId, corpId);
            empDepRefs.add(empDepRef);
        }
        return empDepRefs;
    }

    private EmpDepRef getDepEmp(PersonnelDTO.UserlistDTO dto, EmpDepRef empDepRef, long depId, String corpId) {
        empDepRef.setCorpId(corpId);
        empDepRef.setDepId(depId);
        empDepRef.setEmpId(dto.getUserid());
        empDepRef.setDelFlag(BusinessConstant.IS_NOT_DEL_FLAG);
        empDepRef.setUpdateTime(Timestamp.valueOf(LocalDateTime.now()));
        return empDepRef;
    }


    private void buildEmpDepRefMethod(List<EmpDepRef> addEmpDepRefList, String corpId, Long synDepartmentId, String userid) {
        EmpDepRef empDepRef = new EmpDepRef();
        empDepRef.setCorpId(corpId);
        empDepRef.setDepId(synDepartmentId);
        empDepRef.setEmpId(userid);
        empDepRef.setDelFlag(BusinessConstant.IS_NOT_DEL_FLAG);
        addEmpDepRefList.add(empDepRef);
    }


}

